/*
 * Simon.cpp
 *
 *  Created on: 12-08-2013
 *      Author: Krzychu
 */

#include "Simon.h"

#include "Config.h"

#include <vector>
#include <stdio.h>
#include <string.h>

Simon::Simon()
{

}

Simon::~Simon()
{

}

std::string Simon::getName()
{
	return "Simon";
}

int Simon::getTransition(std::string pattern, int p, List** L, char c)
{
	List* cell;
	int patternSize = pattern.size();

	if (p < patternSize - 1 && pattern[p + 1] == c)
	{
		return(p + 1);
	}
	else if (p > -1)
	{
		cell = L[p];
		while (cell != NULL)
		{
			if (pattern[cell->element] == c)
			{
				return(cell->element);
			}
			else
			{
				cell = cell->next;
				return(-1);
			}
		}
	}

	return -1;
}


void Simon::setTransition(int p, int q, List** L)
{
	List* cell;

	cell = new List();
	cell->element = q;
	cell->next = L[p];
	L[p] = cell;
}

int Simon::preSimon(std::string pattern,  List** L)
{
	int i, k, ell;
	List* cell;

	int patternSize = pattern.size();

	ell = -1;
	for (i = 1; i < patternSize; ++i)
	{
		k = ell;
		cell = (ell == -1 ? NULL : L[k]);
		ell = -1;

		if (pattern[i] == pattern[k + 1])
		{
			ell = k + 1;
		}
		else
		{
			setTransition(i - 1, k + 1, L);
		}

		while (cell != NULL)
		{
			k = cell->element;
			if (pattern[i] == pattern[k])
			{
				ell = k;
			}
			else
			{
				setTransition(i - 1, k, L);
			}
			cell = cell->next;
		}
	}

	return(ell);
}

searchResult Simon::execute(std::string text, std::string pattern)
{
	searchResult result;
	result.iterationsTaken = 0;

	int textSize = text.size();
	int patternSize = pattern.size();

	int j, ell, state;
	List** L = new List*[patternSize];
	for (int i = 0; i < patternSize; i++)
	{
		L[i] = new List();
	}

	/* Preprocessing */
	ell = preSimon(pattern, L);

	/* Searching */
	for (state = -1, j = 0; j < textSize; ++j)
	{
		result.iterationsTaken++;

		state = getTransition(pattern, state, L, text[j]);
		if (state >= patternSize - 1)
		{
			result.positionsFound.push_back(j - patternSize + 1);
			if (Config::getInstance()->getFindOneOccurence())
			{
				return result;
			}

			state = ell;
		}
	}

	return result;
}

